-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[ADT] Define countr_zero in terms of popcount (NFC) #158519
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ADT] Define countr_zero in terms of popcount (NFC) #158519
Conversation
We can express the fallback mechanism of llvm::countr_zero a lot more concisely with llvm::popcount. Since llvm::countr_zero now requires llvm::popcount, this patch moves llvm::popcount earlier.
@llvm/pr-subscribers-llvm-adt Author: Kazu Hirata (kazutakahirata) ChangesWe can express the fallback mechanism of llvm::countr_zero a lot more Full diff: https://github.com/llvm/llvm-project/pull/158519.diff 1 Files Affected:
diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index d6e33c3e6133a..73aa84504d6e9 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -148,6 +148,35 @@ template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
return (Value != 0) && ((Value & (Value - 1)) == 0);
}
+/// Count the number of set bits in a value.
+/// Ex. popcount(0xF000F000) = 8
+/// Returns 0 if the word is zero.
+template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
+[[nodiscard]] inline int popcount(T Value) noexcept {
+ if constexpr (sizeof(T) <= 4) {
+#if defined(__GNUC__)
+ return (int)__builtin_popcount(Value);
+#else
+ uint32_t v = Value;
+ v = v - ((v >> 1) & 0x55555555);
+ v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
+ return int(((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24);
+#endif
+ } else if constexpr (sizeof(T) <= 8) {
+#if defined(__GNUC__)
+ return (int)__builtin_popcountll(Value);
+#else
+ uint64_t v = Value;
+ v = v - ((v >> 1) & 0x5555555555555555ULL);
+ v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
+ v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
+ return int((uint64_t)(v * 0x0101010101010101ULL) >> 56);
+#endif
+ } else {
+ static_assert(sizeof(T) == 0, "T must be 8 bytes or less");
+ }
+}
+
/// Count number of 0's from the least significant bit to the most
/// stopping at the first 1.
///
@@ -179,19 +208,9 @@ template <typename T> [[nodiscard]] int countr_zero(T Val) {
#endif
}
- // Fall back to the bisection method.
- unsigned ZeroBits = 0;
- T Shift = std::numeric_limits<T>::digits >> 1;
- T Mask = std::numeric_limits<T>::max() >> Shift;
- while (Shift) {
- if ((Val & Mask) == 0) {
- Val >>= Shift;
- ZeroBits |= Shift;
- }
- Shift >>= 1;
- Mask >>= Shift;
- }
- return ZeroBits;
+ // Fallback to popcount. "(Val & -Val) - 1" is a bitmask with all bits below
+ // the least significant 1 set.
+ return llvm::popcount(static_cast<std::make_unsigned_t<T>>((Val & -Val) - 1));
}
/// Count number of 0's from the most significant bit to the least
@@ -300,35 +319,6 @@ template <typename T> [[nodiscard]] T bit_ceil(T Value) {
return T(1) << llvm::bit_width<T>(Value - 1u);
}
-/// Count the number of set bits in a value.
-/// Ex. popcount(0xF000F000) = 8
-/// Returns 0 if the word is zero.
-template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
-[[nodiscard]] inline int popcount(T Value) noexcept {
- if constexpr (sizeof(T) <= 4) {
-#if defined(__GNUC__)
- return (int)__builtin_popcount(Value);
-#else
- uint32_t v = Value;
- v = v - ((v >> 1) & 0x55555555);
- v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
- return int(((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24);
-#endif
- } else if constexpr (sizeof(T) <= 8) {
-#if defined(__GNUC__)
- return (int)__builtin_popcountll(Value);
-#else
- uint64_t v = Value;
- v = v - ((v >> 1) & 0x5555555555555555ULL);
- v = (v & 0x3333333333333333ULL) + ((v >> 2) & 0x3333333333333333ULL);
- v = (v + (v >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
- return int((uint64_t)(v * 0x0101010101010101ULL) >> 56);
-#endif
- } else {
- static_assert(sizeof(T) == 0, "T must be 8 bytes or less");
- }
-}
-
// Forward-declare rotr so that rotl can use it.
template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
[[nodiscard]] constexpr T rotr(T V, int R);
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/162/builds/31095 Here is the relevant piece of the build log for the reference
|
/// Returns 0 if Value is zero. | ||
template <typename T> | ||
[[nodiscard]] inline int popcount(T Value) noexcept { | ||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned integer type"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My suggestion was that we allow unsigned types and call make_unsigned as a part of the implementation. Would there be any issues with that?
We can express the fallback mechanism of llvm::countr_zero a lot more concisely with llvm::popcount. Since llvm::countr_zero now requires llvm::popcount, this patch moves llvm::popcount earlier.
We can express the fallback mechanism of llvm::countr_zero a lot more
concisely with llvm::popcount. Since llvm::countr_zero now requires
llvm::popcount, this patch moves llvm::popcount earlier.